/*
 * Copyright (C) 2010-2011 sunjumper@163.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package mfinder.impl;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import mf.mfrpc.util.LocationUtil;
import mfinder.MFinderException;
import mfinder.annotation.Action;
import mfinder.annotation.Result;
import mfinder.annotation.WebService;
import mfinder.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * WSActionFactory继承DefaultActionFactory，覆写了Action的映射路径。
 */
public class WSActionFactory extends DefaultActionFactory {

    /** 日志 */
    private static final Logger LOG = LoggerFactory.getLogger(WSActionFactory.class);

    /**
     * 添加Action。
     *
     * @param obj 包含WebService注解的类对象或实例对象。
     *
     * @see mfinder.annotation.WebService
     */
    @Override
    public void addActions(Object obj) {
        //判断传入参数为类对象或实例对象
        boolean isCls = obj instanceof Class;
        Class<?> cls = isCls ? (Class) obj : obj.getClass();

        WebService ws = cls.getAnnotation(WebService.class);
        //WebService注解类
        if (ws != null) {
            Object invoker = isCls ? null : obj;
            Class[] interfaces = ws.interfaceClass();
            //注入多个interface或继承类
            for (Class iclass : interfaces) {
                Method[] ms = iclass.getDeclaredMethods();
                for (Method m : ms) {
                    int mod = m.getModifiers();
                    //public/protected的对象方法, 无静态方法
                    if ((Modifier.isPublic(mod) || Modifier.isProtected(mod))
                            && !Modifier.isStatic(mod)) {
                        m.setAccessible(true);
                        if (isCls && invoker == null) {
                            try {
                                invoker = cls.newInstance();
                            } catch (Exception e) {
                                throw new MFinderException(e, null);
                            }
                        }
                        //the same object
                        addAction(createActionProxy(ws, m, invoker));
                    }
                }
            }
        }
    }

    /**
     * 创建Action代理类。
     *
     * @param ws WebService注解。
     * @param method 接口方法。
     * @param obj 实现对象。
     *
     * @return ActionProxy Action代理类。
     */
    private ActionProxy createActionProxy(WebService ws, Method method, Object obj) {
        //WebService's name isn't null
        String name = PATH + StringUtil.trim(ws.name(), PATH);

        //映射路径
        String path = LocationUtil.locate(name, method);

        //add extension
        path = afterPathCreation(path);

        //实现的方法
        Method called = null;
        try {
            called = obj.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
        } catch (Exception e) {
            throw new MFinderException(e, null);
        }

        //support action's interceptorStack and results
        Action action = called.getAnnotation(Action.class);
        ActionProxy ap = new ActionProxy(name, path, action, method, obj);

        //interceptorStack
        String stackName = action == null ? null : action.interceptorStack();
        boolean hasStackName = !StringUtil.isNull(stackName);
        //1st namespace check
        if (!hasStackName) {
            //WebService interceptorStack
            stackName = ws.interceptorStack();
            //defaultInterceptorStack
            if (StringUtil.isNull(stackName))
                stackName = getDefaultInterceptorStack();

            hasStackName = !StringUtil.isNull(stackName);
        }

        //2nd namespace check
        if (hasStackName) {
            InterceptorStackProxy isp = getInterceptorStacks().get(stackName);
            if (isp == null) {
                LOG.warn("no such InterceptorStack [" + stackName + "] defined in : " + ap.getMethodInfo());
                //ap.setInterceptorStack(Collections.EMPTY_LIST);
            } else {
                ap.setInterceptors(isp.getInterceptors());
            }
        }

        //set results
        if (action != null) {
            Result[] rs = action.results();
            Map<String, Result> res = new HashMap<String, Result>(rs.length);
            for (Result r : rs) {
                res.put(r.name(), r);
            }
            ap.setResults(res);
        } else {
            ap.setResults(Collections.EMPTY_MAP);
        }

        return ap;
    }
}
